glossarist 2.8.12 → 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: 284d64ec4a9bac75c0639dd3254a715e881d46949240f430db9d6322cec09379
4
- data.tar.gz: a349a57dba2f2e5b79c93561dcfa2fd54ee1627326d66b3cf2d6b66cbcca2f3c
3
+ metadata.gz: 807c7a2aface582e6a1dfb6c4dd63f09c51d7429274597d8b7ee48d63282b7a9
4
+ data.tar.gz: 5af5e853bb00cec098806db6dbbe03b91dd5177a06388f4e9d79255dbf43cc2e
5
5
  SHA512:
6
- metadata.gz: 270a84779887cf795087a0fb56f923240b26c028bef18c6b436b5129cfcc0985a020e9ab5b2ce2b2b6e5f472f75de7c79546d56fe0a94465f068d3f5ee28617a
7
- data.tar.gz: 1411213954c8fbbdb95bc9e5c4bc8455d55edf4df1944714dcd8a0ffac4733d64ed16bd32ae6c259691739dda04418fe9bcf91ba2af04451505a0b0d61168c55
6
+ metadata.gz: 4020f1eabf8c0915ef3463e646abc71929d71ebe1fedbf0691ef008581b68c8c6913f9a908612fc2863e2c495bdc4ef643a4390ac6a2bf3dde4b7441b172bfdd
7
+ data.tar.gz: 69a9f82209f07c81bb8f7a68fdb1e556b3cc4d7180630f99f88f889ce84e81d37e18b9d643b8b35aaed4f0391f890841c51c468a0da4edc96ace4d22d1859f95
@@ -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
@@ -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.12"
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.12
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-15 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