glossarist 2.8.11 → 2.8.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c5365426551b5a43eb67d58bd80ec429cc27c7c2d374f45abb7911da3dde329
4
- data.tar.gz: 86394c15f0f7c8db4101682aeb99f2c44aa89f79012c931a59365d3426488a39
3
+ metadata.gz: 807c7a2aface582e6a1dfb6c4dd63f09c51d7429274597d8b7ee48d63282b7a9
4
+ data.tar.gz: 5af5e853bb00cec098806db6dbbe03b91dd5177a06388f4e9d79255dbf43cc2e
5
5
  SHA512:
6
- metadata.gz: 7434ab207294cbbf27f257a3368e7a7e588594d87fd6a3c896668625625c587dc135609664991e7ebd91077429a7d3bc6a7f2aee46d015666048be32a4f830ba
7
- data.tar.gz: 745e77b7e667e43fd05d1b96ab44d44c808092f5c2547e95ffa8d84bca1115d21a0d8756d917eaefc024ba78cf51f2e4818e077b0a74492d2b1383304043a256
6
+ metadata.gz: 4020f1eabf8c0915ef3463e646abc71929d71ebe1fedbf0691ef008581b68c8c6913f9a908612fc2863e2c495bdc4ef643a4390ac6a2bf3dde4b7441b172bfdd
7
+ data.tar.gz: 69a9f82209f07c81bb8f7a68fdb1e556b3cc4d7180630f99f88f889ce84e81d37e18b9d643b8b35aaed4f0391f890841c51c468a0da4edc96ace4d22d1859f95
data/config.yml CHANGED
@@ -39,10 +39,17 @@ designation:
39
39
 
40
40
  related_concept:
41
41
  type:
42
- # Lifecycle (ISO 10241-1)
42
+ # Lifecycle (ISO 10241-1 / ISO 19135)
43
43
  - deprecates
44
+ - deprecated_by
44
45
  - supersedes
45
46
  - superseded_by
47
+ - replaces
48
+ - replaced_by
49
+ - invalidates
50
+ - invalidated_by
51
+ - retires
52
+ - retired_by
46
53
  # Hierarchical (ISO 10241-1 / ISO 25964)
47
54
  - narrower
48
55
  - broader
@@ -52,9 +59,13 @@ related_concept:
52
59
  # Hierarchical sub-types — ISO 25964 partitive (BTP/NTP)
53
60
  - broader_partitive
54
61
  - narrower_partitive
62
+ - has_part
63
+ - is_part_of
55
64
  # Hierarchical sub-types — ISO 25964 instantial (BTI/NTI)
56
65
  - broader_instantial
57
66
  - narrower_instantial
67
+ - instance_of
68
+ - has_instance
58
69
  # Equivalence (ISO 10241-1 / ISO 25964 exactMatch / SKOS)
59
70
  - equivalent
60
71
  - exact_match
@@ -69,17 +80,30 @@ related_concept:
69
80
  - contrast
70
81
  # Associative (ISO 10241-1 / ISO 25964 RT / TBX crossReference)
71
82
  - see
83
+ - references
72
84
  # Associative sub-types (ISO 25964 / TBX)
73
85
  - related_concept
74
86
  - related_concept_broader
75
87
  - related_concept_narrower
76
88
  # Associative — spatial/temporal (ISO 25964 / TBX)
77
- - sequentially_related_concept
78
- - spatially_related_concept
79
- - temporally_related_concept
89
+ - sequentially_related
90
+ - spatially_related
91
+ - temporally_related
80
92
  # Lexical (ISO 12620 / TBX)
81
93
  - homograph
82
94
  - false_friend
95
+ # Register management (ISO 19135)
96
+ - has_concept
97
+ - is_concept_of
98
+ - has_definition
99
+ - definition_of
100
+ - inherits
101
+ - inherited_by
102
+ # Versioning / definitional (ISO 19135)
103
+ - has_version
104
+ - version_of
105
+ - current_version
106
+ - current_version_of
83
107
 
84
108
  iso12620:
85
109
  term_type:
@@ -17,7 +17,7 @@ module Glossarist
17
17
 
18
18
  key_value do
19
19
  map :term, to: :term
20
- map :concept_id, to: :concept_id
20
+ map %i[concept_id id], to: :concept_id
21
21
  map :source, to: :source
22
22
  map :ref_type, to: :ref_type
23
23
  map :urn, to: :urn
@@ -56,6 +56,37 @@ module Glossarist
56
56
  nil
57
57
  end
58
58
 
59
+ # Returns all ancestor section IDs for a given section, from immediate
60
+ # parent up to the root. Implements the cascading membership semantics:
61
+ # a concept in section "3.1.1" is also a member of "3.1" and "3".
62
+ # (concept-model: gloss:hasChildSection / gloss:hasParentSection are
63
+ # owl:TransitiveProperty.)
64
+ def section_ancestor_ids(target_id)
65
+ @section_parent_index ||= build_section_parent_index
66
+ @section_parent_index[target_id] || []
67
+ end
68
+
69
+ # Returns all section IDs that a concept belongs to, including
70
+ # transitive ancestor sections (cascading membership).
71
+ #
72
+ # Resolution order (concept-model convention):
73
+ # 1. Explicit domains[] entries with ref_type: "section"
74
+ # 2. Term-ID-prefix derivation fallback (longest registered prefix)
75
+ #
76
+ # @param concept [ManagedConcept, #data] the concept to resolve
77
+ # @return [Array<String>] section IDs, child-first then ancestors
78
+ def concept_section_ids(concept)
79
+ explicit = explicit_section_ids(concept)
80
+ ids = explicit.empty? ? derive_section_ids_from_id(concept) : explicit
81
+ ids.flat_map { |id| [id] + section_ancestor_ids(id) }.uniq
82
+ end
83
+
84
+ # Returns true if the given section ID exists anywhere in the section
85
+ # tree (root or descendant).
86
+ def section_exists?(target_id)
87
+ !section_by_id(target_id).nil?
88
+ end
89
+
59
90
  def self.from_file(path)
60
91
  return nil unless File.exist?(path)
61
92
 
@@ -68,5 +99,53 @@ module Glossarist
68
99
 
69
100
  from_file(register_path)
70
101
  end
102
+
103
+ private
104
+
105
+ # Build a flat child→[ancestor_ids] index by walking the section tree.
106
+ # Ancestors are ordered immediate-parent-first (closest section first).
107
+ # @example for sections [{id:"3", children:[{id:"3.1"}]}]:
108
+ # { "3.1" => ["3"] }
109
+ def build_section_parent_index
110
+ index = {}
111
+ walk_section_tree(sections, []) do |section, ancestors|
112
+ index[section.id] = ancestors.reverse unless ancestors.empty?
113
+ end
114
+ index
115
+ end
116
+
117
+ def walk_section_tree(nodes, ancestors, &block)
118
+ Array(nodes).each do |section|
119
+ yield section, ancestors
120
+ child_ancestors = ancestors + [section.id]
121
+ walk_section_tree(section.children, child_ancestors, &block)
122
+ end
123
+ end
124
+
125
+ def explicit_section_ids(concept)
126
+ domains = concept.respond_to?(:data) ? concept.data&.domains : nil
127
+ Array(domains).select { |d| d.ref_type == "section" }
128
+ .filter_map(&:concept_id)
129
+ end
130
+
131
+ # Term-ID-prefix derivation: when a concept has no explicit section
132
+ # domains, derive section membership from its identifier using the
133
+ # longest registered section prefix.
134
+ # @example "103-01-01" with section "103" registered → ["103"]
135
+ def derive_section_ids_from_id(concept)
136
+ concept_id = concept.respond_to?(:data) ? concept.data&.id : nil
137
+ return [] unless concept_id
138
+
139
+ all_section_ids = collect_all_section_ids
140
+ all_section_ids.select { |sid| concept_id.start_with?("#{sid}-", "#{sid}.") }
141
+ .max_by(&:length)
142
+ &.then { |sid| [sid] } || []
143
+ end
144
+
145
+ def collect_all_section_ids
146
+ ids = []
147
+ walk_section_tree(sections, []) { |section, _| ids << section.id }
148
+ ids
149
+ end
71
150
  end
72
151
  end
@@ -27,16 +27,17 @@ module Glossarist
27
27
  related_match: [Namespaces::SkosNamespace, :relatedMatch],
28
28
  see: [Namespaces::SkosNamespace, :related],
29
29
  deprecates: [Namespaces::GlossaristNamespace, :deprecates],
30
+ deprecated_by: [Namespaces::GlossaristNamespace, :deprecatedBy],
30
31
  supersedes: [Namespaces::GlossaristNamespace, :supersedes],
31
32
  superseded_by: [Namespaces::GlossaristNamespace, :supersededBy],
32
33
  compare: [Namespaces::GlossaristNamespace, :compares],
33
34
  contrast: [Namespaces::GlossaristNamespace, :contrasts],
34
- sequentially_related_concept: [Namespaces::GlossaristNamespace,
35
- :sequentiallyRelated],
36
- spatially_related_concept: [Namespaces::GlossaristNamespace,
37
- :spatiallyRelated],
38
- temporally_related_concept: [Namespaces::GlossaristNamespace,
39
- :temporallyRelated],
35
+ sequentially_related: [Namespaces::GlossaristNamespace,
36
+ :sequentiallyRelated],
37
+ spatially_related: [Namespaces::GlossaristNamespace,
38
+ :spatiallyRelated],
39
+ temporally_related: [Namespaces::GlossaristNamespace,
40
+ :temporallyRelated],
40
41
  related_concept_broader: [Namespaces::GlossaristNamespace,
41
42
  :relatedConceptBroader],
42
43
  related_concept_narrower: [Namespaces::GlossaristNamespace,
@@ -31,6 +31,13 @@ module Glossarist
31
31
  nil
32
32
  end
33
33
 
34
+ # Loads the dataset's register.yaml as a DatasetRegister (with
35
+ # hierarchical sections, urn, ordering). Returns nil if no
36
+ # register.yaml exists or it uses the legacy format.
37
+ def dataset_register
38
+ @dataset_register ||= load_dataset_register
39
+ end
40
+
34
41
  def bibliography_index
35
42
  @bibliography_index ||= BibliographyIndex.build_from_concepts(
36
43
  concepts, dataset_path: @path
@@ -98,6 +105,13 @@ module Glossarist
98
105
  end
99
106
  index
100
107
  end
108
+
109
+ def load_dataset_register
110
+ reg_path = File.join(@path, "register.yaml")
111
+ return nil unless File.exist?(reg_path)
112
+
113
+ DatasetRegister.from_file(reg_path)
114
+ end
101
115
  end
102
116
  end
103
117
  end
@@ -27,6 +27,21 @@ module Glossarist
27
27
  suggestion: "Provide at least concept_id or urn for the domain reference",
28
28
  )
29
29
  end
30
+
31
+ next unless domain.ref_type == "section" && domain.concept_id
32
+
33
+ register = context.collection_context.dataset_register
34
+ next unless register
35
+
36
+ unless register.section_exists?(domain.concept_id)
37
+ issues << issue(
38
+ "domain #{idx + 1} references section '#{domain.concept_id}' " \
39
+ "that does not exist in register.yaml",
40
+ location: fname,
41
+ suggestion: "Add section '#{domain.concept_id}' to " \
42
+ "register.yaml or fix the reference",
43
+ )
44
+ end
30
45
  end
31
46
 
32
47
  issues
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Glossarist
7
- VERSION = "2.8.11"
7
+ VERSION = "2.8.13"
8
8
  end
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.8.11
4
+ version: 2.8.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-06-14 00:00:00.000000000 Z
11
+ date: 2026-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model