glossarist 2.12.0 → 2.13.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/CLAUDE.md +42 -0
- data/config.yml +31 -0
- data/lib/glossarist/designation/designation_relationship.rb +1 -1
- data/lib/glossarist/glossary_definition.rb +6 -0
- data/lib/glossarist/managed_concept.rb +2 -0
- data/lib/glossarist/rdf/gloss_concept.rb +3 -0
- data/lib/glossarist/rdf/gloss_partitive_member.rb +33 -0
- data/lib/glossarist/rdf/gloss_partitive_relation.rb +38 -0
- data/lib/glossarist/rdf/gloss_type_shared_plurality.rb +31 -0
- data/lib/glossarist/rdf/v3.rb +3 -0
- data/lib/glossarist/rdf.rb +4 -0
- data/lib/glossarist/related_concept.rb +1 -1
- data/lib/glossarist/schema_migration/v2_to_v3.rb +19 -5
- data/lib/glossarist/transforms/concept_to_gloss_transform.rb +56 -0
- data/lib/glossarist/v3/managed_concept.rb +2 -0
- data/lib/glossarist/v3/managed_concept_data.rb +15 -1
- data/lib/glossarist/v3/partitive_member.rb +61 -0
- data/lib/glossarist/v3/partitive_relation.rb +119 -0
- data/lib/glossarist/v3/type_shared_plurality.rb +42 -0
- data/lib/glossarist/v3.rb +6 -0
- data/lib/glossarist/validation/rules/partitive_relation_rule.rb +150 -0
- data/lib/glossarist/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85ff1a35136e9b7ff920830b2bf9a2e4b477bffed405c2386e33739ed6560a45
|
|
4
|
+
data.tar.gz: f7ef8fb96a58eb21a71534fcc8afc1cf7f26053379cfb00e5ec98098620ca6a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: beeecf5f816d5ec10741375d357a1b3602e7d415ebedfc71eff52d414f1d623ed119c7995355231c147020f54b814dc8306d37acdc6eecacdf8ca72ec6ddd65f
|
|
7
|
+
data.tar.gz: 0a7de5bd20f0f7c58c1c08066983b5c490999570249b686f84f60bd87df9b5e436913db66e43b5d1003805bfa33a6613d67bbd8c49373d21a03815dde83a8e42
|
data/CLAUDE.md
CHANGED
|
@@ -35,6 +35,48 @@ All model classes use `Lutaml::Model::Serializable` for serialization.
|
|
|
35
35
|
- **`ConceptReference`** (`concept_reference.rb`) — a typed reference to another concept. Local refs use `concept_id` alone; external refs use `source` (URN prefix) + `concept_id` or a direct `urn` field. Has `local?`/`external?` predicates. No `to_gcr_hash` or `from_urn` — model-driven architecture uses lutaml-model for serialization; URN construction/parsing belongs in `UrnResolver`.
|
|
36
36
|
- **`RelatedConcept`** (`related_concept.rb`) — a concept with a typed relationship. Relationship types cover ISO 10241-1 (deprecates/supersedes/superseded_by/compare/contrast/see), ISO 25964/SKOS (broader/narrower/broader_generic/narrower_generic/broader_partitive/narrower_partitive/broader_instantial/narrower_instantial/equivalent/close_match/broad_match/narrow_match/related_match), ISO 12620/TBX (homograph/false_friend/related_concept/related_concept_broader/related_concept_narrower/sequentially_related_concept/spatially_related_concept/temporally_related_concept), and designation-level (abbreviated_form_for/short_form_for). Types are defined in `config.yml` under `related_concept.type`.
|
|
37
37
|
|
|
38
|
+
#### V3 `related` placement (MECE)
|
|
39
|
+
|
|
40
|
+
In V3, `related` lives **only** on `V3::ManagedConcept#related`.
|
|
41
|
+
`V3::ManagedConceptData` does NOT declare or serialize `related`.
|
|
42
|
+
V2 placed `related` inside data; the `SchemaMigration::V2ToV3#step_v2_to_v3`
|
|
43
|
+
migration moves any data-level entries up to the concept level. The
|
|
44
|
+
V3 output never carries `related` at the data level.
|
|
45
|
+
|
|
46
|
+
This consolidation closes the legacy trap where writing to
|
|
47
|
+
`data.related` bypassed `ManagedConcept.detect_schema_version` (which
|
|
48
|
+
keys off `concept.related`).
|
|
49
|
+
|
|
50
|
+
### PartitiveRelation (V3 only)
|
|
51
|
+
|
|
52
|
+
`PartitiveRelation` (`v3/partitive_relation.rb`) — an ISO 704 / ISO 1087-1 / ISO 12620 partitive relation. One comprehensive concept (superordinate concept partitive) is connected to two or more partitive concepts (subordinate concepts partitive) which fitted together constitute the comprehensive.
|
|
53
|
+
|
|
54
|
+
Each PartitiveRelation carries:
|
|
55
|
+
|
|
56
|
+
- `comprehensive` — ConceptRef to the superordinate concept partitive (the whole)
|
|
57
|
+
- `partitives` — `PartitiveMember [2..*]` (ISO 704 "two or more"); each member wraps a ConceptRef plus optional `MemberCertainty` (`confirmed` default, `possible`)
|
|
58
|
+
- `completeness` — `complete` (default; the encoded partitives constitute the whole) or `partial` (others exist beyond those encoded). ISO 704 rake backline notation.
|
|
59
|
+
- `plurality` — optional `TypeSharedPlurality` block (ISO 704 close-set double line promoted to data: `is_shared`, `is_uncertain`, `shared_type`). Replaces the prior opaque `markers` field.
|
|
60
|
+
- `criterion` — optional localized string hash (`{ "eng" => "physical structure" }`). ISO 12620 coordinate-concept coherence: partitives within one relation share the comprehensive AND the criterion.
|
|
61
|
+
|
|
62
|
+
Wired into `V3::ManagedConcept#partitive_relations`. NOT on `ManagedConceptData` (relationships live at the concept level for MECE consistency with `related`).
|
|
63
|
+
|
|
64
|
+
Enum values are SSOT-loaded from `config.yml` via `GlossaryDefinition::COMPLETENESS_VALUES` and `MEMBER_CERTAINTY_VALUES`. The `values:` option on each attribute documents the enum; the model's `validate!` method enforces structural invariants after construction (empty comprehensive, <2 partitives, self-loops, incoherent plurality, invalid enum values).
|
|
65
|
+
|
|
66
|
+
Semantic checks (≥2 partitives cardinality, duplicate comprehensive+criterion detection, criterion-absent warning, ExternalConcept shape) live in `Validation::Rules::PartitiveRelationRule` (auto-registered by `Validation::Rules`, code GLS-221).
|
|
67
|
+
|
|
68
|
+
RDF emission: `Rdf::GlossPartitiveRelation` view class with companion `GlossPartitiveMember` and `GlossTypeSharedPlurality`. Wired into `Transforms::ConceptToGlossTransform` and emitted as `gloss:hasPartitiveRelation` link from `GlossConcept`.
|
|
69
|
+
|
|
70
|
+
Binary `broader_partitive`/`narrower_partitive`/`has_part`/`is_part_of` `RelatedConcept` edges and `PartitiveRelation` coexist — no automatic consolidation. Use a binary edge for pairwise relationships; use a PartitiveRelation when membership is jointly significant (≥2 partitives + completeness/plurality/criterion metadata).
|
|
71
|
+
|
|
72
|
+
#### ExternalConcept
|
|
73
|
+
|
|
74
|
+
`status: external` is a value of `ConceptStatus` (not a separate class). A concept with `status: external` is referenced from this dataset but defined elsewhere — ISO 704 "parenthetic term" semantic role. Required fields (definition, sources) are relaxed; at least one designation is still required. Resolves to another dataset's concept via `provided_by` when a defining dataset is loaded.
|
|
75
|
+
|
|
76
|
+
#### Relationship type extensions
|
|
77
|
+
|
|
78
|
+
Two new values in `RelatedConceptType`: `provides` (real concept → external concept) and `provided_by` (external concept → real concept). Inverse pair, established at the collection level.
|
|
79
|
+
|
|
38
80
|
### Reference Resolution
|
|
39
81
|
|
|
40
82
|
- **`ReferenceExtractor`** (`reference_extractor.rb`) — extracts `ConceptReference` and `AssetReference` objects from `{{...}}` mentions and `image::...[]` references in concept text fields.
|
data/config.yml
CHANGED
|
@@ -101,6 +101,12 @@ related_concept:
|
|
|
101
101
|
- version_of
|
|
102
102
|
- current_version
|
|
103
103
|
- current_version_of
|
|
104
|
+
# ExternalConcept resolution (Glossarist extension)
|
|
105
|
+
# Real concept `provides` substance for an external concept;
|
|
106
|
+
# external concept `provided_by` real concept. Inverse pair.
|
|
107
|
+
# Established at the collection level when both datasets are loaded.
|
|
108
|
+
- provides
|
|
109
|
+
- provided_by
|
|
104
110
|
|
|
105
111
|
iso12620:
|
|
106
112
|
term_type:
|
|
@@ -178,6 +184,10 @@ concept:
|
|
|
178
184
|
- valid
|
|
179
185
|
- superseded
|
|
180
186
|
- retired
|
|
187
|
+
# Glossarist extension: concept referenced from this dataset but
|
|
188
|
+
# defined elsewhere. Resolves via `provided_by` when a defining
|
|
189
|
+
# dataset is loaded. ISO 704 "parenthetic term" semantic role.
|
|
190
|
+
- external
|
|
181
191
|
|
|
182
192
|
concept_date:
|
|
183
193
|
type:
|
|
@@ -186,3 +196,24 @@ concept_date:
|
|
|
186
196
|
- retired
|
|
187
197
|
- review
|
|
188
198
|
- review_decision
|
|
199
|
+
|
|
200
|
+
# PartitiveRelation redesign (TODO.partitive-relation-v2)
|
|
201
|
+
# Replaces partitive_enumeration + plurality_marker.
|
|
202
|
+
|
|
203
|
+
completeness:
|
|
204
|
+
# Whether the encoded partitives fitted together constitute the
|
|
205
|
+
# whole comprehensive concept (complete) or only some of it
|
|
206
|
+
# (partial — other partitives exist but are not encoded).
|
|
207
|
+
# ISO 704: rake backline ending with a tooth = complete;
|
|
208
|
+
# backline continuing without a tooth = partial.
|
|
209
|
+
# Default: complete.
|
|
210
|
+
value:
|
|
211
|
+
- complete
|
|
212
|
+
- partial
|
|
213
|
+
|
|
214
|
+
member_certainty:
|
|
215
|
+
# Per-partitive certainty. Glossarist extension beyond ISO 704
|
|
216
|
+
# notation. Defaults to confirmed when omitted.
|
|
217
|
+
value:
|
|
218
|
+
- confirmed
|
|
219
|
+
- possible
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Glossarist
|
|
4
4
|
module Designation
|
|
5
5
|
class DesignationRelationship < Lutaml::Model::Serializable
|
|
6
|
-
attribute :content, :
|
|
6
|
+
attribute :content, :hash
|
|
7
7
|
attribute :type, :string,
|
|
8
8
|
values: Glossarist::GlossaryDefinition::DESIGNATION_RELATIONSHIP_TYPES
|
|
9
9
|
attribute :target, :string
|
|
@@ -33,5 +33,11 @@ module Glossarist
|
|
|
33
33
|
"relationship_type")&.freeze
|
|
34
34
|
|
|
35
35
|
ISO12620_TERM_TYPES = config.dig("iso12620", "term_type").freeze
|
|
36
|
+
|
|
37
|
+
# PartitiveRelation redesign (TODO.partitive-relation-v2).
|
|
38
|
+
# Replaces PARTITIVE_ENUMERATION_VALUES + PLURALITY_MARKER_VALUES.
|
|
39
|
+
COMPLETENESS_VALUES = config.dig("completeness", "value").freeze
|
|
40
|
+
|
|
41
|
+
MEMBER_CERTAINTY_VALUES = config.dig("member_certainty", "value").freeze
|
|
36
42
|
end
|
|
37
43
|
end
|
|
@@ -174,6 +174,8 @@ module Glossarist
|
|
|
174
174
|
return "3" if concept.related&.any?
|
|
175
175
|
return "3" if concept.sources&.any?
|
|
176
176
|
return "3" if concept.data&.domains&.any?
|
|
177
|
+
return "3" if concept.is_a?(V3::ManagedConcept) &&
|
|
178
|
+
concept.partitive_relations&.any?
|
|
177
179
|
return "3" if localization_has_references?(concept)
|
|
178
180
|
|
|
179
181
|
"2"
|
|
@@ -11,6 +11,7 @@ module Glossarist
|
|
|
11
11
|
attribute :sources, GlossConceptSource, collection: true
|
|
12
12
|
attribute :domains, GlossConceptReference, collection: true
|
|
13
13
|
attribute :dates, GlossConceptDate, collection: true
|
|
14
|
+
attribute :partitive_relations, GlossPartitiveRelation, collection: true
|
|
14
15
|
|
|
15
16
|
RelationshipPredicates::CONCEPT_REL_PREDICATES.each_key do |type|
|
|
16
17
|
attribute :"#{type}_targets", :string, collection: true
|
|
@@ -41,6 +42,8 @@ module Glossarist
|
|
|
41
42
|
link: "gloss:hasDomain"
|
|
42
43
|
members :dates,
|
|
43
44
|
link: "gloss:hasDate"
|
|
45
|
+
members :partitive_relations,
|
|
46
|
+
link: "gloss:hasPartitiveRelation"
|
|
44
47
|
|
|
45
48
|
RelationshipPredicates::CONCEPT_REL_PREDICATES.each do |type, (ns, name)|
|
|
46
49
|
predicate name, namespace: ns, to: :"#{type}_targets",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Rdf
|
|
7
|
+
# RDF view for V3::PartitiveMember. Emits a
|
|
8
|
+
# gloss:PartitiveMember subject with ref + certainty properties.
|
|
9
|
+
class GlossPartitiveMember < Lutaml::Model::Serializable
|
|
10
|
+
attribute :ref_id, :string
|
|
11
|
+
attribute :ref_source, :string
|
|
12
|
+
attribute :ref_text, :string
|
|
13
|
+
attribute :certainty, :string
|
|
14
|
+
|
|
15
|
+
rdf do
|
|
16
|
+
namespace Namespaces::GlossaristNamespace
|
|
17
|
+
|
|
18
|
+
subject { |m| "partitiveMember/#{m.ref_source}:#{m.ref_id}" }
|
|
19
|
+
|
|
20
|
+
types "gloss:PartitiveMember"
|
|
21
|
+
|
|
22
|
+
predicate :refSource, namespace: Namespaces::GlossaristNamespace,
|
|
23
|
+
to: :ref_source
|
|
24
|
+
predicate :refId, namespace: Namespaces::GlossaristNamespace,
|
|
25
|
+
to: :ref_id
|
|
26
|
+
predicate :refText, namespace: Namespaces::GlossaristNamespace,
|
|
27
|
+
to: :ref_text
|
|
28
|
+
predicate :certainty, namespace: Namespaces::GlossaristNamespace,
|
|
29
|
+
to: :certainty
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Rdf
|
|
7
|
+
# RDF view for V3::PartitiveRelation. Emits a
|
|
8
|
+
# gloss:PartitiveRelation subject with comprehensive, hasPartitive,
|
|
9
|
+
# completeness, hasPlurality, criterion properties.
|
|
10
|
+
class GlossPartitiveRelation < Lutaml::Model::Serializable
|
|
11
|
+
attribute :identifier, :string
|
|
12
|
+
attribute :comprehensive_uri, :string
|
|
13
|
+
attribute :partitive_member_ids, :string, collection: true
|
|
14
|
+
attribute :completeness, :string
|
|
15
|
+
attribute :has_plurality, GlossTypeSharedPlurality
|
|
16
|
+
attribute :criterion, :hash
|
|
17
|
+
|
|
18
|
+
rdf do
|
|
19
|
+
namespace Namespaces::GlossaristNamespace
|
|
20
|
+
|
|
21
|
+
subject { |r| "partitiveRelation/#{r.identifier}/#{r.object_id}" }
|
|
22
|
+
|
|
23
|
+
types "gloss:PartitiveRelation"
|
|
24
|
+
|
|
25
|
+
predicate :comprehensive, namespace: Namespaces::GlossaristNamespace,
|
|
26
|
+
to: :comprehensive_uri, uri_reference: true
|
|
27
|
+
predicate :hasPartitive, namespace: Namespaces::GlossaristNamespace,
|
|
28
|
+
to: :partitive_member_ids, uri_reference: true
|
|
29
|
+
predicate :completeness, namespace: Namespaces::GlossaristNamespace,
|
|
30
|
+
to: :completeness, uri_reference: true
|
|
31
|
+
members :has_plurality,
|
|
32
|
+
link: "gloss:hasPlurality"
|
|
33
|
+
predicate :criterion, namespace: Namespaces::GlossaristNamespace,
|
|
34
|
+
to: :criterion
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
|
|
5
|
+
module Glossarist
|
|
6
|
+
module Rdf
|
|
7
|
+
# RDF view for V3::TypeSharedPlurality. Emits a
|
|
8
|
+
# gloss:TypeSharedPlurality subject with is_shared, is_uncertain,
|
|
9
|
+
# shared_type properties.
|
|
10
|
+
class GlossTypeSharedPlurality < Lutaml::Model::Serializable
|
|
11
|
+
attribute :is_shared, :boolean
|
|
12
|
+
attribute :is_uncertain, :boolean
|
|
13
|
+
attribute :shared_type_uri, :string
|
|
14
|
+
|
|
15
|
+
rdf do
|
|
16
|
+
namespace Namespaces::GlossaristNamespace
|
|
17
|
+
|
|
18
|
+
subject { |p| "plurality/#{p.object_id}" }
|
|
19
|
+
|
|
20
|
+
types "gloss:TypeSharedPlurality"
|
|
21
|
+
|
|
22
|
+
predicate :isShared, namespace: Namespaces::GlossaristNamespace,
|
|
23
|
+
to: :is_shared
|
|
24
|
+
predicate :isUncertain, namespace: Namespaces::GlossaristNamespace,
|
|
25
|
+
to: :is_uncertain
|
|
26
|
+
predicate :sharedType, namespace: Namespaces::GlossaristNamespace,
|
|
27
|
+
to: :shared_type_uri, uri_reference: true
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/glossarist/rdf/v3.rb
CHANGED
data/lib/glossarist/rdf.rb
CHANGED
|
@@ -42,6 +42,10 @@ module Glossarist
|
|
|
42
42
|
autoload :GlossFigureImage, "#{__dir__}/rdf/gloss_figure_image"
|
|
43
43
|
autoload :GlossTable, "#{__dir__}/rdf/gloss_table"
|
|
44
44
|
autoload :GlossFormula, "#{__dir__}/rdf/gloss_formula"
|
|
45
|
+
autoload :GlossPartitiveRelation, "#{__dir__}/rdf/gloss_partitive_relation"
|
|
46
|
+
autoload :GlossPartitiveMember, "#{__dir__}/rdf/gloss_partitive_member"
|
|
47
|
+
autoload :GlossTypeSharedPlurality,
|
|
48
|
+
"#{__dir__}/rdf/gloss_type_shared_plurality"
|
|
45
49
|
autoload :V3, "#{__dir__}/rdf/v3"
|
|
46
50
|
end
|
|
47
51
|
end
|
|
@@ -38,11 +38,25 @@ module Glossarist
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def self.step_v2_to_v3(concept)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
# V2 placed `related` inside ManagedConceptData; V3 places it
|
|
42
|
+
# on ManagedConcept itself. Move any data-level related entries
|
|
43
|
+
# up to the concept level. V3::ManagedConceptData no longer
|
|
44
|
+
# serializes `related`, so we don't need to clear the moved-from
|
|
45
|
+
# slot — the V3 output naturally omits it.
|
|
46
|
+
data_related = concept.data&.related
|
|
47
|
+
return step_v2_to_v3_hyperedge_note if data_related.nil? || data_related.empty?
|
|
48
|
+
|
|
49
|
+
concept.related ||= []
|
|
50
|
+
concept.related = (concept.related + data_related).uniq
|
|
51
|
+
"3"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Hyperedges: V2 has no hyperedge concept. V2's binary
|
|
55
|
+
# `broader_partitive` / `narrower_partitive` edges migrate as
|
|
56
|
+
# `related` entries (see step_v2_to_v3). Hyperedges are a V3-only
|
|
57
|
+
# construct and require no migration logic — concepts upgraded
|
|
58
|
+
# from V2 to V3 simply have no hyperedges.
|
|
59
|
+
def self.step_v2_to_v3_hyperedge_note
|
|
46
60
|
"3"
|
|
47
61
|
end
|
|
48
62
|
end
|
|
@@ -130,10 +130,66 @@ module Glossarist
|
|
|
130
130
|
domains: build_gloss_domains(managed_concept.data&.domains,
|
|
131
131
|
identifier),
|
|
132
132
|
dates: build_gloss_dates(managed_concept.dates, identifier),
|
|
133
|
+
partitive_relations: build_gloss_partitive_relations(
|
|
134
|
+
v3_partitive_relations(managed_concept), identifier,
|
|
135
|
+
),
|
|
133
136
|
**rel_targets,
|
|
134
137
|
)
|
|
135
138
|
end
|
|
136
139
|
|
|
140
|
+
# PartitiveRelations are a V3-only attribute. The transform
|
|
141
|
+
# accepts both V1/V2 and V3 concepts; for V1/V2 concepts the
|
|
142
|
+
# relation list is always empty.
|
|
143
|
+
def v3_partitive_relations(managed_concept)
|
|
144
|
+
return [] unless managed_concept.is_a?(V3::ManagedConcept)
|
|
145
|
+
|
|
146
|
+
managed_concept.partitive_relations
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def build_gloss_partitive_relations(relations, identifier)
|
|
150
|
+
return [] unless relations
|
|
151
|
+
|
|
152
|
+
Array(relations).map do |rel|
|
|
153
|
+
Rdf::GlossPartitiveRelation.new(
|
|
154
|
+
identifier: identifier.to_s,
|
|
155
|
+
comprehensive_uri: partitive_concept_uri(rel.comprehensive),
|
|
156
|
+
partitive_member_ids: Array(rel.partitives).map do |m|
|
|
157
|
+
partitive_member_uri(m)
|
|
158
|
+
end,
|
|
159
|
+
completeness: rel.completeness,
|
|
160
|
+
has_plurality: build_gloss_plurality(rel.plurality),
|
|
161
|
+
criterion: rel.criterion,
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def build_gloss_plurality(plurality)
|
|
167
|
+
return nil unless plurality
|
|
168
|
+
|
|
169
|
+
Rdf::GlossTypeSharedPlurality.new(
|
|
170
|
+
is_shared: plurality.is_shared,
|
|
171
|
+
is_uncertain: plurality.is_uncertain,
|
|
172
|
+
shared_type_uri: partitive_concept_uri(plurality.shared_type),
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def partitive_concept_uri(ref)
|
|
177
|
+
return nil unless ref.is_a?(Glossarist::ConceptRef)
|
|
178
|
+
|
|
179
|
+
Glossarist::Rdf::Namespaces::GlossaristNamespace.uri +
|
|
180
|
+
"concept/#{ref.id || ref.text}"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def partitive_member_uri(member)
|
|
184
|
+
return nil unless member.is_a?(V3::PartitiveMember)
|
|
185
|
+
|
|
186
|
+
ref = member.ref
|
|
187
|
+
return nil unless ref.is_a?(Glossarist::ConceptRef)
|
|
188
|
+
|
|
189
|
+
Glossarist::Rdf::Namespaces::GlossaristNamespace.uri +
|
|
190
|
+
"concept/#{ref.id || ref.text}"
|
|
191
|
+
end
|
|
192
|
+
|
|
137
193
|
def build_gloss_localized_concept(l10n, concept_id)
|
|
138
194
|
lang = l10n.language_code
|
|
139
195
|
data = l10n.data
|
|
@@ -5,6 +5,7 @@ module Glossarist
|
|
|
5
5
|
class ManagedConcept < Glossarist::ManagedConcept
|
|
6
6
|
attribute :data, V3::ManagedConceptData, default: -> { V3::ManagedConceptData.new }
|
|
7
7
|
attribute :related, V3::RelatedConcept, collection: true
|
|
8
|
+
attribute :partitive_relations, V3::PartitiveRelation, collection: true
|
|
8
9
|
attribute :dates, V3::ConceptDate, collection: true
|
|
9
10
|
attribute :date_accepted, V3::ConceptDate
|
|
10
11
|
attribute :sources, V3::ConceptSource, collection: true
|
|
@@ -12,6 +13,7 @@ module Glossarist
|
|
|
12
13
|
key_value do
|
|
13
14
|
map :data, to: :data
|
|
14
15
|
map :related, to: :related
|
|
16
|
+
map :partitive_relations, to: :partitive_relations
|
|
15
17
|
map :dates, to: :dates
|
|
16
18
|
map %i[date_accepted dateAccepted],
|
|
17
19
|
with: { from: :date_accepted_from_yaml, to: :date_accepted_to_yaml }
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
module Glossarist
|
|
4
4
|
module V3
|
|
5
|
+
# V3 ManagedConceptData — the data payload inside a V3::ManagedConcept.
|
|
6
|
+
#
|
|
7
|
+
# V3 placement rule (MECE): `related` lives ONLY on V3::ManagedConcept,
|
|
8
|
+
# not on its data payload. V2 placed `related` inside data; the
|
|
9
|
+
# V2→V3 migration (SchemaMigration::V2ToV3) moves it to the concept
|
|
10
|
+
# level. Keeping `related` writable on V3 data would re-open the
|
|
11
|
+
# trap where writing to `data.related` bypasses
|
|
12
|
+
# `ManagedConcept.detect_schema_version` (which keys off
|
|
13
|
+
# `concept.related`).
|
|
14
|
+
#
|
|
15
|
+
# The base class Glossarist::ManagedConceptData still declares
|
|
16
|
+
# `related` for V1/V2 compatibility; V3 overrides that with an
|
|
17
|
+
# empty attribute via `attribute :related, nil` so the slot is
|
|
18
|
+
# inert and serializes nothing.
|
|
5
19
|
class ManagedConceptData < Glossarist::ManagedConceptData
|
|
6
20
|
attribute :sources, V3::ConceptSource, collection: true
|
|
7
21
|
attribute :localizations, V3::LocalizedConcept,
|
|
@@ -17,7 +31,6 @@ module Glossarist
|
|
|
17
31
|
with: { from: :domains_from_yaml, to: :domains_to_yaml }
|
|
18
32
|
map :tags, to: :tags
|
|
19
33
|
map :sources, to: :sources
|
|
20
|
-
map :related, to: :related
|
|
21
34
|
map :localizations, to: :localizations,
|
|
22
35
|
with: { from: :localizations_from_yaml, to: :localizations_to_yaml }
|
|
23
36
|
end
|
|
@@ -34,3 +47,4 @@ module Glossarist
|
|
|
34
47
|
end
|
|
35
48
|
end
|
|
36
49
|
end
|
|
50
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module V3
|
|
5
|
+
# PartitiveMember — one member of a PartitiveRelation, carrying
|
|
6
|
+
# a ConceptRef to the partitive concept plus optional certainty
|
|
7
|
+
# metadata (Glossarist extension beyond ISO 704 notation).
|
|
8
|
+
#
|
|
9
|
+
# A PartitiveMember with no certainty field is implicitly
|
|
10
|
+
# confirmed.
|
|
11
|
+
class PartitiveMember < Lutaml::Model::Serializable
|
|
12
|
+
DEFAULT_CERTAINTY = "confirmed"
|
|
13
|
+
|
|
14
|
+
attribute :ref, ConceptRef
|
|
15
|
+
attribute :certainty, :string,
|
|
16
|
+
values: Glossarist::GlossaryDefinition::MEMBER_CERTAINTY_VALUES,
|
|
17
|
+
default: -> { DEFAULT_CERTAINTY }
|
|
18
|
+
|
|
19
|
+
key_value do
|
|
20
|
+
map :ref, to: :ref
|
|
21
|
+
map :certainty, to: :certainty
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validate!
|
|
25
|
+
validate_ref!
|
|
26
|
+
validate_certainty!
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def confirmed?
|
|
31
|
+
certainty == DEFAULT_CERTAINTY
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def possible?
|
|
35
|
+
certainty == "possible"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def validate_ref!
|
|
41
|
+
return if ref.is_a?(ConceptRef) && (ref.source || ref.id || ref.text)
|
|
42
|
+
|
|
43
|
+
raise ArgumentError,
|
|
44
|
+
"PartitiveMember#ref must be a non-empty ConceptRef " \
|
|
45
|
+
"(source, id, or text required)"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def validate_certainty!
|
|
49
|
+
return if certainty.nil?
|
|
50
|
+
|
|
51
|
+
unless Glossarist::GlossaryDefinition::MEMBER_CERTAINTY_VALUES
|
|
52
|
+
.include?(certainty)
|
|
53
|
+
raise ArgumentError,
|
|
54
|
+
"PartitiveMember#certainty has invalid value " \
|
|
55
|
+
"#{certainty.inspect}; must be one of " \
|
|
56
|
+
"#{GlossaryDefinition::MEMBER_CERTAINTY_VALUES.join(', ')}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module V3
|
|
5
|
+
# PartitiveRelation — an ISO 704 / ISO 1087-1 / ISO 12620
|
|
6
|
+
# partitive relation connecting a comprehensive concept
|
|
7
|
+
# (superordinate concept partitive) to two or more partitive
|
|
8
|
+
# concepts (subordinate concepts partitive) which fitted together
|
|
9
|
+
# constitute the comprehensive.
|
|
10
|
+
#
|
|
11
|
+
# Shown as a rake or bracket in source diagrams. All partitives
|
|
12
|
+
# within one relation are coordinate concepts: they share the
|
|
13
|
+
# comprehensive AND share the criterion of subdivision.
|
|
14
|
+
#
|
|
15
|
+
# Glossarist extensions beyond ISO notation:
|
|
16
|
+
# - Per-partitive certainty (MemberCertainty)
|
|
17
|
+
# - plurality.shared_type (the type the partitives share)
|
|
18
|
+
# - criterion as a structured field (not just diagrammatic)
|
|
19
|
+
#
|
|
20
|
+
# Replaces the prior PartitiveHyperedge class. The "hyperedge"
|
|
21
|
+
# framing was graph-theoretic; ISO calls this a *relation*.
|
|
22
|
+
class PartitiveRelation < Lutaml::Model::Serializable
|
|
23
|
+
DEFAULT_COMPLETENESS = "complete"
|
|
24
|
+
|
|
25
|
+
attribute :comprehensive, ConceptRef
|
|
26
|
+
attribute :partitives, PartitiveMember, collection: true
|
|
27
|
+
attribute :completeness, :string,
|
|
28
|
+
values: Glossarist::GlossaryDefinition::COMPLETENESS_VALUES,
|
|
29
|
+
default: -> { DEFAULT_COMPLETENESS }
|
|
30
|
+
attribute :plurality, TypeSharedPlurality
|
|
31
|
+
attribute :criterion, :hash
|
|
32
|
+
|
|
33
|
+
key_value do
|
|
34
|
+
map :comprehensive, to: :comprehensive
|
|
35
|
+
map :partitives, to: :partitives
|
|
36
|
+
map :completeness, to: :completeness
|
|
37
|
+
map :plurality, to: :plurality
|
|
38
|
+
map :criterion, to: :criterion
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def validate!
|
|
42
|
+
validate_comprehensive!
|
|
43
|
+
validate_partitives!
|
|
44
|
+
validate_self_loop!
|
|
45
|
+
validate_completeness!
|
|
46
|
+
validate_plurality!
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def complete?
|
|
51
|
+
completeness == "complete"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def partial?
|
|
55
|
+
completeness == "partial"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# ISO 704: a partitive relation connects to two or more
|
|
59
|
+
# partitives. A single binary has_part edge is not a
|
|
60
|
+
# PartitiveRelation.
|
|
61
|
+
def coordinate?
|
|
62
|
+
partitives.length >= 2
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def validate_comprehensive!
|
|
68
|
+
return if comprehensive.is_a?(ConceptRef) &&
|
|
69
|
+
(comprehensive.source || comprehensive.id || comprehensive.text)
|
|
70
|
+
|
|
71
|
+
raise ArgumentError,
|
|
72
|
+
"PartitiveRelation#comprehensive must be a non-empty " \
|
|
73
|
+
"ConceptRef (source, id, or text required)"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def validate_partitives!
|
|
77
|
+
if partitives.empty?
|
|
78
|
+
raise ArgumentError, "PartitiveRelation requires at least one partitive"
|
|
79
|
+
end
|
|
80
|
+
unless coordinate?
|
|
81
|
+
raise ArgumentError,
|
|
82
|
+
"PartitiveRelation requires ≥2 partitives (ISO 704); a single " \
|
|
83
|
+
"binary has_part edge should be used instead"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
partitives.each(&:validate!)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def validate_self_loop!
|
|
90
|
+
return unless comprehensive.is_a?(ConceptRef)
|
|
91
|
+
|
|
92
|
+
comp_key = [comprehensive.source, comprehensive.id]
|
|
93
|
+
partitives.each do |member|
|
|
94
|
+
next unless member.ref.is_a?(ConceptRef)
|
|
95
|
+
next unless [member.ref.source, member.ref.id] == comp_key
|
|
96
|
+
|
|
97
|
+
raise ArgumentError,
|
|
98
|
+
"PartitiveRelation#partitives cannot include the comprehensive"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def validate_completeness!
|
|
103
|
+
return if completeness.nil?
|
|
104
|
+
|
|
105
|
+
unless Glossarist::GlossaryDefinition::COMPLETENESS_VALUES
|
|
106
|
+
.include?(completeness)
|
|
107
|
+
raise ArgumentError,
|
|
108
|
+
"PartitiveRelation#completeness has invalid value " \
|
|
109
|
+
"#{completeness.inspect}; must be one of " \
|
|
110
|
+
"#{GlossaryDefinition::COMPLETENESS_VALUES.join(', ')}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def validate_plurality!
|
|
115
|
+
plurality&.validate!
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module V3
|
|
5
|
+
# TypeSharedPlurality — semantic claims encoded by ISO 704's
|
|
6
|
+
# close-set double line and broken line notation, promoted from
|
|
7
|
+
# diagram-notation flags to structured data so tools can reason
|
|
8
|
+
# about plurality directly.
|
|
9
|
+
#
|
|
10
|
+
# A PartitiveRelation has at most one TypeSharedPlurality block.
|
|
11
|
+
# Absent means: no type-shared plurality claim is being made.
|
|
12
|
+
#
|
|
13
|
+
# Replaces the prior `markers` field (which encoded the same
|
|
14
|
+
# information as opaque strings like "double" and "dashed").
|
|
15
|
+
class TypeSharedPlurality < Lutaml::Model::Serializable
|
|
16
|
+
attribute :is_shared, :boolean
|
|
17
|
+
attribute :is_uncertain, :boolean, default: -> { false }
|
|
18
|
+
attribute :shared_type, ConceptRef
|
|
19
|
+
|
|
20
|
+
key_value do
|
|
21
|
+
map :is_shared, to: :is_shared
|
|
22
|
+
map :is_uncertain, to: :is_uncertain
|
|
23
|
+
map :shared_type, to: :shared_type
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def validate!
|
|
27
|
+
unless is_shared.is_a?(TrueClass) || is_shared.is_a?(FalseClass)
|
|
28
|
+
raise ArgumentError,
|
|
29
|
+
"TypeSharedPlurality#is_shared is required (boolean)"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if is_uncertain == true && is_shared == false
|
|
33
|
+
raise ArgumentError,
|
|
34
|
+
"TypeSharedPlurality#is_uncertain requires is_shared: true " \
|
|
35
|
+
"(ISO 704 broken line qualifies the close-set double line claim)"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/glossarist/v3.rb
CHANGED
|
@@ -9,6 +9,9 @@ module Glossarist
|
|
|
9
9
|
autoload :DetailedDefinition, "glossarist/v3/detailed_definition"
|
|
10
10
|
autoload :ConceptRef, "glossarist/v3/concept_ref"
|
|
11
11
|
autoload :RelatedConcept, "glossarist/v3/related_concept"
|
|
12
|
+
autoload :PartitiveRelation, "glossarist/v3/partitive_relation"
|
|
13
|
+
autoload :PartitiveMember, "glossarist/v3/partitive_member"
|
|
14
|
+
autoload :TypeSharedPlurality, "glossarist/v3/type_shared_plurality"
|
|
12
15
|
autoload :ConceptData, "glossarist/v3/concept_data"
|
|
13
16
|
autoload :LocalizedConcept, "glossarist/v3/localized_concept"
|
|
14
17
|
autoload :ManagedConceptData, "glossarist/v3/managed_concept_data"
|
|
@@ -23,6 +26,9 @@ module Glossarist
|
|
|
23
26
|
Configuration.register_model(LocalizedConcept, id: :localized_concept)
|
|
24
27
|
Configuration.register_model(ConceptRef, id: :concept_ref)
|
|
25
28
|
Configuration.register_model(RelatedConcept, id: :related_concept)
|
|
29
|
+
Configuration.register_model(PartitiveRelation, id: :partitive_relation)
|
|
30
|
+
Configuration.register_model(PartitiveMember, id: :partitive_member)
|
|
31
|
+
Configuration.register_model(TypeSharedPlurality, id: :type_shared_plurality)
|
|
26
32
|
Configuration.register_model(ManagedConceptData, id: :managed_concept_data)
|
|
27
33
|
Configuration.register_model(ManagedConcept, id: :managed_concept)
|
|
28
34
|
Configuration.register_model(ConceptDocument, id: :concept_document)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Validation
|
|
5
|
+
module Rules
|
|
6
|
+
# Validates semantic invariants of PartitiveRelation entries
|
|
7
|
+
# that the model constructor does NOT enforce. Specifically:
|
|
8
|
+
#
|
|
9
|
+
# - error when a relation has fewer than 2 partitives
|
|
10
|
+
# (ISO 704: "two or more"; single binary should use
|
|
11
|
+
# has_part edge instead)
|
|
12
|
+
# - error when two relations share the same comprehensive
|
|
13
|
+
# AND the same non-nil criterion (duplicate decomposition;
|
|
14
|
+
# ISO 12620 coordinate-concept coherence)
|
|
15
|
+
# - warning when a relation has no criterion (cannot
|
|
16
|
+
# distinguish from siblings sharing the comprehensive)
|
|
17
|
+
# - error when plurality.is_uncertain is set without
|
|
18
|
+
# plurality.is_shared: true (broken-line qualifies
|
|
19
|
+
# close-set double line)
|
|
20
|
+
# - error when ExternalConcept (status: external) lacks
|
|
21
|
+
# at least one designation
|
|
22
|
+
#
|
|
23
|
+
# The model constructor already rejects empty comprehensive,
|
|
24
|
+
# empty partitives list, self-loops, invalid enum values.
|
|
25
|
+
class PartitiveRelationRule < Base
|
|
26
|
+
def code = "GLS-221"
|
|
27
|
+
def category = :schema
|
|
28
|
+
def severity = "error"
|
|
29
|
+
def scope = :concept
|
|
30
|
+
|
|
31
|
+
def applicable?(context)
|
|
32
|
+
concept = context.concept
|
|
33
|
+
return false unless concept.is_a?(V3::ManagedConcept)
|
|
34
|
+
|
|
35
|
+
concept.partitive_relations&.any? || external?(concept)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def check(context)
|
|
39
|
+
concept = context.concept
|
|
40
|
+
fname = context.file_name
|
|
41
|
+
issues = []
|
|
42
|
+
|
|
43
|
+
return issues unless concept.is_a?(V3::ManagedConcept)
|
|
44
|
+
|
|
45
|
+
relations = Array(concept.partitive_relations)
|
|
46
|
+
|
|
47
|
+
relations.each_with_index do |rel, idx|
|
|
48
|
+
check_cardinality(rel, idx, fname, issues)
|
|
49
|
+
check_criterion_present(rel, idx, fname, issues)
|
|
50
|
+
check_plurality_coherence(rel, idx, fname, issues)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
check_duplicate_decomposition(relations, fname, issues)
|
|
54
|
+
check_external_concept(concept, fname, issues)
|
|
55
|
+
|
|
56
|
+
issues
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def external?(concept)
|
|
62
|
+
concept.status == "external"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def check_cardinality(rel, idx, fname, issues)
|
|
66
|
+
return if rel.partitives.length >= 2
|
|
67
|
+
|
|
68
|
+
issues << issue(
|
|
69
|
+
"partitive_relation #{idx + 1} has fewer than 2 partitives " \
|
|
70
|
+
"(ISO 704 requires two or more); a single binary has_part edge " \
|
|
71
|
+
"should be used instead",
|
|
72
|
+
location: fname,
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def check_criterion_present(rel, idx, fname, issues)
|
|
77
|
+
return if rel.criterion && !rel.criterion.empty?
|
|
78
|
+
|
|
79
|
+
issues << issue(
|
|
80
|
+
"partitive_relation #{idx + 1} has no criterion; cannot verify " \
|
|
81
|
+
"distinctness from sibling relations sharing the comprehensive " \
|
|
82
|
+
"(ISO 12620 coordinate-concept coherence)",
|
|
83
|
+
severity: "warning",
|
|
84
|
+
location: fname,
|
|
85
|
+
suggestion: "Add a criterion: { eng: '...' } field",
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def check_plurality_coherence(rel, idx, fname, issues)
|
|
90
|
+
plural = rel.plurality
|
|
91
|
+
return unless plural
|
|
92
|
+
return unless plural.is_uncertain
|
|
93
|
+
return if plural.is_shared
|
|
94
|
+
|
|
95
|
+
issues << issue(
|
|
96
|
+
"partitive_relation #{idx + 1}.plurality: is_uncertain requires " \
|
|
97
|
+
"is_shared: true (ISO 704 broken line qualifies the close-set " \
|
|
98
|
+
"double line claim)",
|
|
99
|
+
location: fname,
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def check_duplicate_decomposition(relations, fname, issues)
|
|
104
|
+
grouped = {}
|
|
105
|
+
relations.each_with_index do |rel, idx|
|
|
106
|
+
next unless rel.criterion && !rel.criterion.empty?
|
|
107
|
+
|
|
108
|
+
key = criterion_key(rel)
|
|
109
|
+
(grouped[key] ||= []) << idx
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
grouped.each do |key, idxs|
|
|
113
|
+
next if idxs.length == 1
|
|
114
|
+
|
|
115
|
+
issues << issue(
|
|
116
|
+
"duplicate PartitiveRelation for comprehensive " \
|
|
117
|
+
"#{key.first.inspect} with criterion #{key.last.inspect} " \
|
|
118
|
+
"(relations ##{idxs.map { |i| i + 1 }.join(', ')}); " \
|
|
119
|
+
"two relations sharing comprehensive AND criterion are the " \
|
|
120
|
+
"same decomposition",
|
|
121
|
+
location: fname,
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def criterion_key(rel)
|
|
127
|
+
comp = rel.comprehensive
|
|
128
|
+
comp_id = comp.is_a?(ConceptRef) ? [comp.source, comp.id] : nil
|
|
129
|
+
[comp_id, rel.criterion]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def check_external_concept(concept, fname, issues)
|
|
133
|
+
return unless external?(concept)
|
|
134
|
+
|
|
135
|
+
has_designation = concept.localizations.any? do |_, lc|
|
|
136
|
+
lc.is_a?(LocalizedConcept) && lc.terms&.any?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
return if has_designation
|
|
140
|
+
|
|
141
|
+
issues << issue(
|
|
142
|
+
"ExternalConcept (status: external) must have at least one " \
|
|
143
|
+
"designation — even external concepts have a name",
|
|
144
|
+
location: fname,
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/glossarist/version.rb
CHANGED
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.13.0
|
|
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-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -310,9 +310,12 @@ files:
|
|
|
310
310
|
- lib/glossarist/rdf/gloss_locality.rb
|
|
311
311
|
- lib/glossarist/rdf/gloss_localized_concept.rb
|
|
312
312
|
- lib/glossarist/rdf/gloss_non_verbal_rep.rb
|
|
313
|
+
- lib/glossarist/rdf/gloss_partitive_member.rb
|
|
314
|
+
- lib/glossarist/rdf/gloss_partitive_relation.rb
|
|
313
315
|
- lib/glossarist/rdf/gloss_pronunciation.rb
|
|
314
316
|
- lib/glossarist/rdf/gloss_reference.rb
|
|
315
317
|
- lib/glossarist/rdf/gloss_table.rb
|
|
318
|
+
- lib/glossarist/rdf/gloss_type_shared_plurality.rb
|
|
316
319
|
- lib/glossarist/rdf/localized_literal.rb
|
|
317
320
|
- lib/glossarist/rdf/lutaml_ext.rb
|
|
318
321
|
- lib/glossarist/rdf/namespaces.rb
|
|
@@ -394,7 +397,10 @@ files:
|
|
|
394
397
|
- lib/glossarist/v3/localized_concept.rb
|
|
395
398
|
- lib/glossarist/v3/managed_concept.rb
|
|
396
399
|
- lib/glossarist/v3/managed_concept_data.rb
|
|
400
|
+
- lib/glossarist/v3/partitive_member.rb
|
|
401
|
+
- lib/glossarist/v3/partitive_relation.rb
|
|
397
402
|
- lib/glossarist/v3/related_concept.rb
|
|
403
|
+
- lib/glossarist/v3/type_shared_plurality.rb
|
|
398
404
|
- lib/glossarist/validation.rb
|
|
399
405
|
- lib/glossarist/validation/asset_index.rb
|
|
400
406
|
- lib/glossarist/validation/bibliography_index.rb
|
|
@@ -436,6 +442,7 @@ files:
|
|
|
436
442
|
- lib/glossarist/validation/rules/orphaned_bibliography_rule.rb
|
|
437
443
|
- lib/glossarist/validation/rules/orphaned_images_rule.rb
|
|
438
444
|
- lib/glossarist/validation/rules/orphaned_l10n_files_rule.rb
|
|
445
|
+
- lib/glossarist/validation/rules/partitive_relation_rule.rb
|
|
439
446
|
- lib/glossarist/validation/rules/preferred_term_rule.rb
|
|
440
447
|
- lib/glossarist/validation/rules/ref_shape_rule.rb
|
|
441
448
|
- lib/glossarist/validation/rules/registry.rb
|