unitsdb 2.1.1 → 2.2.2

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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +8 -1
  3. data/.gitignore +2 -0
  4. data/.gitmodules +4 -3
  5. data/.rubocop.yml +13 -8
  6. data/.rubocop_todo.yml +217 -100
  7. data/CLAUDE.md +55 -0
  8. data/Gemfile +4 -1
  9. data/README.adoc +283 -16
  10. data/data/dimensions.yaml +1864 -0
  11. data/data/prefixes.yaml +874 -0
  12. data/data/quantities.yaml +3715 -0
  13. data/data/scales.yaml +97 -0
  14. data/data/schemas/dimensions-schema.yaml +153 -0
  15. data/data/schemas/prefixes-schema.yaml +155 -0
  16. data/data/schemas/quantities-schema.yaml +117 -0
  17. data/data/schemas/scales-schema.yaml +106 -0
  18. data/data/schemas/unit_systems-schema.yaml +116 -0
  19. data/data/schemas/units-schema.yaml +215 -0
  20. data/data/unit_systems.yaml +78 -0
  21. data/data/units.yaml +14052 -0
  22. data/exe/unitsdb +7 -1
  23. data/lib/unitsdb/cli.rb +42 -15
  24. data/lib/unitsdb/commands/_modify.rb +40 -4
  25. data/lib/unitsdb/commands/base.rb +6 -2
  26. data/lib/unitsdb/commands/check_si/si_formatter.rb +488 -0
  27. data/lib/unitsdb/commands/check_si/si_matcher.rb +487 -0
  28. data/lib/unitsdb/commands/check_si/si_ttl_parser.rb +103 -0
  29. data/lib/unitsdb/commands/check_si/si_updater.rb +254 -0
  30. data/lib/unitsdb/commands/check_si.rb +54 -35
  31. data/lib/unitsdb/commands/get.rb +11 -10
  32. data/lib/unitsdb/commands/normalize.rb +21 -7
  33. data/lib/unitsdb/commands/qudt/check.rb +150 -0
  34. data/lib/unitsdb/commands/qudt/formatter.rb +194 -0
  35. data/lib/unitsdb/commands/qudt/matcher.rb +746 -0
  36. data/lib/unitsdb/commands/qudt/ttl_parser.rb +403 -0
  37. data/lib/unitsdb/commands/qudt/update.rb +126 -0
  38. data/lib/unitsdb/commands/qudt/updater.rb +189 -0
  39. data/lib/unitsdb/commands/qudt.rb +82 -0
  40. data/lib/unitsdb/commands/release.rb +12 -9
  41. data/lib/unitsdb/commands/search.rb +12 -11
  42. data/lib/unitsdb/commands/ucum/check.rb +42 -29
  43. data/lib/unitsdb/commands/ucum/formatter.rb +2 -1
  44. data/lib/unitsdb/commands/ucum/matcher.rb +23 -9
  45. data/lib/unitsdb/commands/ucum/update.rb +14 -13
  46. data/lib/unitsdb/commands/ucum/updater.rb +40 -6
  47. data/lib/unitsdb/commands/ucum/xml_parser.rb +0 -2
  48. data/lib/unitsdb/commands/ucum.rb +44 -4
  49. data/lib/unitsdb/commands/validate/identifiers.rb +2 -4
  50. data/lib/unitsdb/commands/validate/qudt_references.rb +111 -0
  51. data/lib/unitsdb/commands/validate/references.rb +36 -19
  52. data/lib/unitsdb/commands/validate/si_references.rb +3 -5
  53. data/lib/unitsdb/commands/validate/ucum_references.rb +105 -0
  54. data/lib/unitsdb/commands/validate.rb +67 -11
  55. data/lib/unitsdb/commands.rb +20 -0
  56. data/lib/unitsdb/config.rb +114 -2
  57. data/lib/unitsdb/database.rb +160 -123
  58. data/lib/unitsdb/dimension.rb +3 -4
  59. data/lib/unitsdb/dimension_details.rb +2 -1
  60. data/lib/unitsdb/dimension_reference.rb +2 -0
  61. data/lib/unitsdb/dimensions.rb +2 -2
  62. data/lib/unitsdb/errors.rb +7 -0
  63. data/lib/unitsdb/external_reference.rb +2 -0
  64. data/lib/unitsdb/identifier.rb +2 -0
  65. data/lib/unitsdb/localized_string.rb +2 -0
  66. data/lib/unitsdb/prefix.rb +2 -4
  67. data/lib/unitsdb/prefix_reference.rb +2 -2
  68. data/lib/unitsdb/prefixes.rb +2 -1
  69. data/lib/unitsdb/quantities.rb +2 -2
  70. data/lib/unitsdb/quantity.rb +2 -6
  71. data/lib/unitsdb/quantity_reference.rb +2 -0
  72. data/lib/unitsdb/qudt.rb +105 -0
  73. data/lib/unitsdb/root_unit_reference.rb +2 -3
  74. data/lib/unitsdb/scale.rb +2 -4
  75. data/lib/unitsdb/scale_properties.rb +2 -0
  76. data/lib/unitsdb/scale_reference.rb +2 -2
  77. data/lib/unitsdb/scales.rb +2 -2
  78. data/lib/unitsdb/si_derived_base.rb +2 -2
  79. data/lib/unitsdb/symbol_presentations.rb +2 -0
  80. data/lib/unitsdb/ucum.rb +21 -10
  81. data/lib/unitsdb/unit.rb +2 -10
  82. data/lib/unitsdb/unit_reference.rb +2 -2
  83. data/lib/unitsdb/unit_system.rb +3 -3
  84. data/lib/unitsdb/unit_system_reference.rb +2 -2
  85. data/lib/unitsdb/unit_systems.rb +2 -2
  86. data/lib/unitsdb/units.rb +2 -2
  87. data/lib/unitsdb/utils.rb +32 -21
  88. data/lib/unitsdb/version.rb +5 -1
  89. data/lib/unitsdb.rb +62 -14
  90. data/unitsdb.gemspec +6 -3
  91. metadata +52 -13
  92. data/lib/unitsdb/commands/si_formatter.rb +0 -485
  93. data/lib/unitsdb/commands/si_matcher.rb +0 -470
  94. data/lib/unitsdb/commands/si_ttl_parser.rb +0 -100
  95. data/lib/unitsdb/commands/si_updater.rb +0 -212
metadata CHANGED
@@ -1,29 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unitsdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-07-11 00:00:00.000000000 Z
11
+ date: 2026-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fuzzy_match
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: lutaml-model
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '0.7'
33
+ version: 0.8.0
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '0.7'
40
+ version: 0.8.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rdf
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -67,19 +81,19 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: '2.3'
69
83
  - !ruby/object:Gem::Dependency
70
- name: terminal-table
84
+ name: table_tennis
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - ">="
87
+ - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '0'
89
+ version: 0.0.7
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - ">="
94
+ - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '0'
96
+ version: 0.0.7
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: thor
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +125,7 @@ files:
111
125
  - ".rspec"
112
126
  - ".rubocop.yml"
113
127
  - ".rubocop_todo.yml"
128
+ - CLAUDE.md
114
129
  - CODE_OF_CONDUCT.md
115
130
  - Gemfile
116
131
  - LICENSE.txt
@@ -118,20 +133,40 @@ files:
118
133
  - Rakefile
119
134
  - bin/console
120
135
  - bin/setup
136
+ - data/dimensions.yaml
137
+ - data/prefixes.yaml
138
+ - data/quantities.yaml
139
+ - data/scales.yaml
140
+ - data/schemas/dimensions-schema.yaml
141
+ - data/schemas/prefixes-schema.yaml
142
+ - data/schemas/quantities-schema.yaml
143
+ - data/schemas/scales-schema.yaml
144
+ - data/schemas/unit_systems-schema.yaml
145
+ - data/schemas/units-schema.yaml
146
+ - data/unit_systems.yaml
147
+ - data/units.yaml
121
148
  - exe/unitsdb
122
149
  - lib/unitsdb.rb
123
150
  - lib/unitsdb/cli.rb
151
+ - lib/unitsdb/commands.rb
124
152
  - lib/unitsdb/commands/_modify.rb
125
153
  - lib/unitsdb/commands/base.rb
126
154
  - lib/unitsdb/commands/check_si.rb
155
+ - lib/unitsdb/commands/check_si/si_formatter.rb
156
+ - lib/unitsdb/commands/check_si/si_matcher.rb
157
+ - lib/unitsdb/commands/check_si/si_ttl_parser.rb
158
+ - lib/unitsdb/commands/check_si/si_updater.rb
127
159
  - lib/unitsdb/commands/get.rb
128
160
  - lib/unitsdb/commands/normalize.rb
161
+ - lib/unitsdb/commands/qudt.rb
162
+ - lib/unitsdb/commands/qudt/check.rb
163
+ - lib/unitsdb/commands/qudt/formatter.rb
164
+ - lib/unitsdb/commands/qudt/matcher.rb
165
+ - lib/unitsdb/commands/qudt/ttl_parser.rb
166
+ - lib/unitsdb/commands/qudt/update.rb
167
+ - lib/unitsdb/commands/qudt/updater.rb
129
168
  - lib/unitsdb/commands/release.rb
130
169
  - lib/unitsdb/commands/search.rb
131
- - lib/unitsdb/commands/si_formatter.rb
132
- - lib/unitsdb/commands/si_matcher.rb
133
- - lib/unitsdb/commands/si_ttl_parser.rb
134
- - lib/unitsdb/commands/si_updater.rb
135
170
  - lib/unitsdb/commands/ucum.rb
136
171
  - lib/unitsdb/commands/ucum/check.rb
137
172
  - lib/unitsdb/commands/ucum/formatter.rb
@@ -141,8 +176,10 @@ files:
141
176
  - lib/unitsdb/commands/ucum/xml_parser.rb
142
177
  - lib/unitsdb/commands/validate.rb
143
178
  - lib/unitsdb/commands/validate/identifiers.rb
179
+ - lib/unitsdb/commands/validate/qudt_references.rb
144
180
  - lib/unitsdb/commands/validate/references.rb
145
181
  - lib/unitsdb/commands/validate/si_references.rb
182
+ - lib/unitsdb/commands/validate/ucum_references.rb
146
183
  - lib/unitsdb/config.rb
147
184
  - lib/unitsdb/database.rb
148
185
  - lib/unitsdb/dimension.rb
@@ -159,6 +196,7 @@ files:
159
196
  - lib/unitsdb/quantities.rb
160
197
  - lib/unitsdb/quantity.rb
161
198
  - lib/unitsdb/quantity_reference.rb
199
+ - lib/unitsdb/qudt.rb
162
200
  - lib/unitsdb/root_unit_reference.rb
163
201
  - lib/unitsdb/scale.rb
164
202
  - lib/unitsdb/scale_properties.rb
@@ -184,6 +222,7 @@ metadata:
184
222
  homepage_uri: https://github.com/unitsml/unitsdb-ruby
185
223
  source_code_uri: https://github.com/unitsml/unitsdb-ruby
186
224
  changelog_uri: https://github.com/unitsml/unitsdb-ruby/releases
225
+ rubygems_mfa_required: 'true'
187
226
  post_install_message:
188
227
  rdoc_options: []
189
228
  require_paths:
@@ -1,485 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "terminal-table"
4
- require_relative "si_ttl_parser"
5
-
6
- module Unitsdb
7
- module Commands
8
- # Formatter for SI check results
9
- module SiFormatter
10
- module_function
11
-
12
- # Display TTL → DB results
13
- def display_si_results(entity_type, matches, missing_matches, unmatched_ttl)
14
- puts "\n=== #{entity_type.capitalize} with matching SI references ==="
15
- if matches.empty?
16
- puts "None"
17
- else
18
- rows = []
19
- matches.each do |match|
20
- si_suffix = SiTtlParser.extract_identifying_suffix(match[:si_uri])
21
- rows << [
22
- "UnitsDB: #{match[:entity_id]}",
23
- "(#{match[:entity_name] || "unnamed"})"
24
- ]
25
- rows << [
26
- "SI TTL: #{si_suffix}",
27
- "(#{match[:si_label] || match[:si_name] || "unnamed"})"
28
- ]
29
- rows << :separator unless match == matches.last
30
- end
31
-
32
- table = Terminal::Table.new(
33
- title: "Valid SI Reference Mappings",
34
- rows: rows
35
- )
36
- puts table
37
- end
38
-
39
- puts "\n=== #{entity_type.capitalize} without SI references ==="
40
- if missing_matches.empty?
41
- puts "None"
42
- else
43
- # Split matches into exact and potential
44
- exact_matches = []
45
- potential_matches = []
46
-
47
- missing_matches.each do |match|
48
- # Get match details
49
- match_details = match[:match_details]
50
- match_desc = match_details&.dig(:match_desc) || ""
51
-
52
- # Symbol matches and partial matches should always be potential matches
53
- if %w[symbol_match partial_match].include?(match_desc)
54
- potential_matches << match
55
- elsif match_details&.dig(:exact) == false
56
- potential_matches << match
57
- else
58
- exact_matches << match
59
- end
60
- end
61
-
62
- # Display exact matches
63
- puts "\n=== Exact Matches (#{exact_matches.size}) ==="
64
- if exact_matches.empty?
65
- puts "None"
66
- else
67
- rows = []
68
- exact_matches.each do |match|
69
- # First row: UnitsDB entity
70
- rows << [
71
- "UnitsDB: #{match[:entity_id]}",
72
- "(#{match[:entity_name] || "unnamed"})"
73
- ]
74
-
75
- # Handle multiple SI matches in a single cell if present
76
- if match[:multiple_si]
77
- # Ensure no duplicate URIs
78
- si_text_parts = []
79
- si_label_parts = []
80
- seen_uris = {}
81
-
82
- match[:multiple_si].each do |si_data|
83
- uri = si_data[:uri]
84
- next if seen_uris[uri] # Skip if we've already seen this URI
85
-
86
- seen_uris[uri] = true
87
-
88
- suffix = SiTtlParser.extract_identifying_suffix(uri)
89
- si_text_parts << suffix
90
- si_label_parts << (si_data[:label] || si_data[:name])
91
- end
92
-
93
- rows << [
94
- "SI TTL: #{si_text_parts.join(", ")}",
95
- "(#{si_label_parts.join(", ")})"
96
- ]
97
- else
98
- # Second row: SI TTL suffix and label/name
99
- si_suffix = SiTtlParser.extract_identifying_suffix(match[:si_uri])
100
- rows << [
101
- "SI TTL: #{si_suffix}",
102
- "(#{match[:si_label] || match[:si_name] || "unnamed"})"
103
- ]
104
- end
105
-
106
- # Status line with match type
107
- match_details = match[:match_details]
108
- match_desc = match_details&.dig(:match_desc) || ""
109
- match_info = format_match_info(match_desc)
110
- status_text = match_info.empty? ? "Missing reference" : "Missing reference (#{match_info})"
111
-
112
- rows << [
113
- "Status: #{status_text}",
114
- "✗"
115
- ]
116
- rows << :separator unless match == exact_matches.last
117
- end
118
-
119
- table = Terminal::Table.new(
120
- title: "Exact Match Missing SI References",
121
- rows: rows
122
- )
123
- puts table
124
- end
125
-
126
- # Display potential matches
127
- puts "\n=== Potential Matches (#{potential_matches.size}) ==="
128
- if potential_matches.empty?
129
- puts "None"
130
- else
131
- rows = []
132
- potential_matches.each do |match|
133
- # First row: UnitsDB entity
134
- rows << [
135
- "UnitsDB: #{match[:entity_id]}",
136
- "(#{match[:entity_name] || "unnamed"})"
137
- ]
138
-
139
- # Handle multiple SI matches in a single cell if present
140
- if match[:multiple_si]
141
- # Ensure no duplicate URIs
142
- si_text_parts = []
143
- seen_uris = {}
144
-
145
- match[:multiple_si].each do |si_data|
146
- uri = si_data[:uri]
147
- next if seen_uris[uri] # Skip if we've already seen this URI
148
-
149
- seen_uris[uri] = true
150
-
151
- suffix = SiTtlParser.extract_identifying_suffix(uri)
152
- si_text_parts << "#{suffix} (#{si_data[:label] || si_data[:name]})"
153
- end
154
-
155
- rows << [
156
- "SI TTL: #{si_text_parts.join(", ")}",
157
- ""
158
- ]
159
- else
160
- # Single TTL entity
161
- si_suffix = SiTtlParser.extract_identifying_suffix(match[:si_uri])
162
- rows << [
163
- "SI TTL: #{si_suffix}",
164
- "(#{match[:si_label] || match[:si_name] || "unnamed"})"
165
- ]
166
- end
167
-
168
- # Status line
169
- match_details = match[:match_details]
170
- match_desc = match_details&.dig(:match_desc) || ""
171
- match_info = format_match_info(match_desc)
172
- status_text = match_info.empty? ? "Missing reference" : "Missing reference"
173
-
174
- rows << [
175
- "Status: #{status_text}",
176
- "✗"
177
- ]
178
- rows << :separator unless match == potential_matches.last
179
- end
180
-
181
- table = Terminal::Table.new(
182
- title: "Potential Match Missing SI References",
183
- rows: rows
184
- )
185
- puts table
186
- end
187
- end
188
-
189
- puts "\n=== SI #{entity_type.capitalize} not mapped to our database ==="
190
- if unmatched_ttl.empty?
191
- puts "None (All TTL entities are referenced - Good job!)"
192
- else
193
- # Group unmatched ttl entities by their URI to avoid duplicates
194
- grouped_unmatched = {}
195
-
196
- unmatched_ttl.each do |entity|
197
- uri = entity[:uri]
198
- grouped_unmatched[uri] = entity unless grouped_unmatched.key?(uri)
199
- end
200
-
201
- rows = []
202
- unique_entities = grouped_unmatched.values
203
-
204
- unique_entities.each do |entity|
205
- # Create the SI TTL row
206
- si_suffix = SiTtlParser.extract_identifying_suffix(entity[:uri])
207
- ttl_row = ["SI TTL: #{si_suffix}", "(#{entity[:label] || entity[:name] || "unnamed"})"]
208
-
209
- rows << ttl_row
210
- rows << [
211
- "Status: No matching UnitsDB entity",
212
- "?"
213
- ]
214
- rows << :separator unless entity == unique_entities.last
215
- end
216
-
217
- table = Terminal::Table.new(
218
- title: "Unmapped SI Entities",
219
- rows: rows
220
- )
221
- puts table
222
- end
223
- end
224
-
225
- # Display DB → TTL results
226
- def display_db_results(entity_type, matches, missing_refs, unmatched_db)
227
- puts "\n=== Summary of database entities referencing SI ==="
228
- puts "#{entity_type.capitalize} with SI references: #{matches.size}"
229
- puts "#{entity_type.capitalize} missing SI references: #{missing_refs.size}"
230
- puts "Database #{entity_type} not matching any SI entity: #{unmatched_db.size}"
231
-
232
- # Show entities with valid references
233
- unless matches.empty?
234
- puts "\n=== #{entity_type.capitalize} with SI references ==="
235
- rows = []
236
- matches.each do |match|
237
- db_entity = match[:db_entity]
238
- entity_id = match[:entity_id] || db_entity.short
239
- entity_name = db_entity.respond_to?(:names) ? db_entity.names&.first : "unnamed"
240
- si_suffix = SiTtlParser.extract_identifying_suffix(match[:ttl_uri])
241
-
242
- ttl_label = match[:ttl_entity] ? (match[:ttl_entity][:label] || match[:ttl_entity][:name]) : "Unknown"
243
-
244
- rows << [
245
- "UnitsDB: #{entity_id}",
246
- "(#{entity_name})"
247
- ]
248
- rows << [
249
- "SI TTL: #{si_suffix}",
250
- "(#{ttl_label})"
251
- ]
252
- rows << :separator unless match == matches.last
253
- end
254
-
255
- table = Terminal::Table.new(
256
- title: "Valid SI References",
257
- rows: rows
258
- )
259
- puts table
260
- end
261
-
262
- puts "\n=== #{entity_type.capitalize} that should reference SI ==="
263
- if missing_refs.empty?
264
- puts "None"
265
- else
266
- # Split missing_refs into exact and potential matches
267
- exact_matches = []
268
- potential_matches = []
269
-
270
- missing_refs.each do |match|
271
- # Determine match type
272
- ttl_entities = match[:ttl_entities]
273
- uri = ttl_entities.first[:uri]
274
- match_type = "Exact match" # Default
275
- match_type = match[:match_types][uri] if match[:match_types] && match[:match_types][uri]
276
-
277
- # Get match description if available
278
- entity_id = match[:db_entity].short
279
- match_pair_key = "#{entity_id}:#{ttl_entities.first[:uri]}"
280
- match_details = Unitsdb::Commands::SiMatcher.instance_variable_get(:@match_details)&.dig(match_pair_key)
281
- match_desc = match_details[:match_desc] if match_details && match_details[:match_desc]
282
-
283
- # Symbol matches and partial matches should always be potential matches
284
- if %w[symbol_match partial_match].include?(match_desc)
285
- potential_matches << match
286
- elsif match_type == "Exact match"
287
- exact_matches << match
288
- else
289
- potential_matches << match
290
- end
291
- end
292
-
293
- # Display exact matches
294
- puts "\n=== Exact Matches (#{exact_matches.size}) ==="
295
- if exact_matches.empty?
296
- puts "None"
297
- else
298
- rows = []
299
- exact_matches.each do |match|
300
- db_entity = match[:db_entity]
301
- entity_id = match[:entity_id] || db_entity.short
302
- entity_name = db_entity.respond_to?(:names) ? db_entity.names&.first : "unnamed"
303
-
304
- # Handle multiple TTL entities in a single row
305
- ttl_entities = match[:ttl_entities]
306
- if ttl_entities.size == 1
307
- # Single TTL entity
308
- ttl_entity = ttl_entities.first
309
- si_suffix = SiTtlParser.extract_identifying_suffix(ttl_entity[:uri])
310
-
311
- rows << [
312
- "UnitsDB: #{entity_id}",
313
- "(#{entity_name})"
314
- ]
315
- rows << [
316
- "SI TTL: #{si_suffix}",
317
- "(#{ttl_entity[:label] || ttl_entity[:name] || "unnamed"})"
318
- ]
319
- else
320
- # Multiple TTL entities, combine them - ensure no duplicates
321
- si_text_parts = []
322
- seen_uris = {}
323
-
324
- ttl_entities.each do |ttl_entity|
325
- uri = ttl_entity[:uri]
326
- next if seen_uris[uri] # Skip if we've already seen this URI
327
-
328
- seen_uris[uri] = true
329
-
330
- suffix = SiTtlParser.extract_identifying_suffix(uri)
331
- si_text_parts << "#{suffix} (#{ttl_entity[:label] || ttl_entity[:name] || "unnamed"})"
332
- end
333
-
334
- si_text = si_text_parts.join(", ")
335
-
336
- rows << [
337
- "UnitsDB: #{entity_id}",
338
- "(#{entity_name})"
339
- ]
340
- rows << [
341
- "SI TTL: #{si_text}",
342
- ""
343
- ]
344
- end
345
-
346
- # Get match details for this match
347
- match_pair_key = "#{db_entity.short}:#{ttl_entities.first[:uri]}"
348
- match_details = Unitsdb::Commands::SiMatcher.instance_variable_get(:@match_details)&.dig(match_pair_key)
349
-
350
- # Format match info
351
- match_info = ""
352
- match_info = format_match_info(match_details[:match_desc]) if match_details
353
-
354
- status_text = match_info.empty? ? "Missing reference" : "Missing reference (#{match_info})"
355
- rows << [
356
- "Status: #{status_text}",
357
- "✗"
358
- ]
359
- rows << :separator unless match == exact_matches.last
360
- end
361
-
362
- table = Terminal::Table.new(
363
- title: "Exact Match Missing SI References",
364
- rows: rows
365
- )
366
- puts table
367
- end
368
-
369
- # Display potential matches
370
- puts "\n=== Potential Matches (#{potential_matches.size}) ==="
371
- if potential_matches.empty?
372
- puts "None"
373
- else
374
- rows = []
375
- potential_matches.each do |match|
376
- db_entity = match[:db_entity]
377
- entity_id = match[:entity_id] || db_entity.short
378
- entity_name = db_entity.respond_to?(:names) ? db_entity.names&.first : "unnamed"
379
-
380
- # Handle multiple TTL entities in a single row
381
- ttl_entities = match[:ttl_entities]
382
- if ttl_entities.size == 1
383
- # Single TTL entity
384
- ttl_entity = ttl_entities.first
385
- si_suffix = SiTtlParser.extract_identifying_suffix(ttl_entity[:uri])
386
-
387
- rows << [
388
- "UnitsDB: #{entity_id}",
389
- "(#{entity_name})"
390
- ]
391
- rows << [
392
- "SI TTL: #{si_suffix}",
393
- "(#{ttl_entity[:label] || ttl_entity[:name] || "unnamed"})"
394
- ]
395
- else
396
- # Multiple TTL entities, combine them - ensure no duplicates
397
- si_text_parts = []
398
- seen_uris = {}
399
-
400
- ttl_entities.each do |ttl_entity|
401
- uri = ttl_entity[:uri]
402
- next if seen_uris[uri] # Skip if we've already seen this URI
403
-
404
- seen_uris[uri] = true
405
-
406
- suffix = SiTtlParser.extract_identifying_suffix(uri)
407
- si_text_parts << "#{suffix} (#{ttl_entity[:label] || ttl_entity[:name] || "unnamed"})"
408
- end
409
-
410
- si_text = si_text_parts.join(", ")
411
-
412
- rows << [
413
- "UnitsDB: #{entity_id}",
414
- "(#{entity_name})"
415
- ]
416
- rows << [
417
- "SI TTL: #{si_text}",
418
- ""
419
- ]
420
- end
421
-
422
- # Get match details
423
- match_pair_key = "#{db_entity.short}:#{ttl_entities.first[:uri]}"
424
- match_details = Unitsdb::Commands::SiMatcher.instance_variable_get(:@match_details)&.dig(match_pair_key)
425
-
426
- # Format match info
427
- match_info = ""
428
- match_info = format_match_info(match_details[:match_desc]) if match_details
429
-
430
- status_text = match_info.empty? ? "Missing reference" : "Missing reference (#{match_info})"
431
- rows << [
432
- "Status: #{status_text}",
433
- "✗"
434
- ]
435
- rows << :separator unless match == potential_matches.last
436
- end
437
-
438
- table = Terminal::Table.new(
439
- title: "Potential Match Missing SI References",
440
- rows: rows
441
- )
442
- puts table
443
- end
444
- end
445
- end
446
-
447
- # Print direction header
448
- def print_direction_header(direction)
449
- case direction
450
- when "SI → UnitsDB"
451
- puts "\n=== Checking SI → UnitsDB (TTL entities referenced by database) ==="
452
- when "UnitsDB → SI"
453
- puts "\n=== Checking UnitsDB → SI (database entities referencing TTL) ==="
454
- end
455
-
456
- puts "\n=== Instructions for #{direction} direction ==="
457
- case direction
458
- when "SI → UnitsDB"
459
- puts "If you are the UnitsDB Register Manager, please ensure that all SI entities have proper references in the UnitsDB database."
460
- puts "For each missing reference, add a reference with the appropriate URI and 'authority: \"si-digital-framework\"'."
461
- when "UnitsDB → SI"
462
- puts "If you are the UnitsDB Register Manager, please add SI references to UnitsDB entities that should have them."
463
- puts "For each entity that should reference SI, add a reference with 'authority: \"si-digital-framework\"' and the SI TTL URI."
464
- end
465
- end
466
-
467
- def set_match_details(details)
468
- @match_details = details
469
- end
470
-
471
- # Format match info for display
472
- def format_match_info(match_desc)
473
- {
474
- "short_to_name" => "short → name",
475
- "short_to_label" => "short → label",
476
- "name_to_name" => "name → name",
477
- "name_to_label" => "name → label",
478
- "name_to_alt_label" => "name → alt_label",
479
- "symbol_match" => "symbol → symbol",
480
- "partial_match" => "partial match"
481
- }[match_desc] || ""
482
- end
483
- end
484
- end
485
- end